#include "board.h"

//Constants and Macros
//------------------------------------------------------------------------------

#define square_to_layer(x, y)           x > 4 ? 1 : 0
#define square_to_index(x, y)           (x > 4 ? x - 5 : x) * 10 + y

#define SHIFT(b, dir) ((b << DIRECTION_SHIFT[dir]) & DIRECTION_MASK[dir])

#define BRANCH_SIZE 100

#define U64 unsigned long long

// mask to keep only the lower 50 bits in a 64 bit number
#define FIFTY_MASK 0x3FFFFFFFFFFFF

//directional constants used to index the bit shift directions
#define UP_LEFT         0
#define UP              1
#define UP_RIGHT        2
#define LEFT            3
#define RIGHT           4
#define DOWN_LEFT       5
#define DOWN            6
#define DOWN_RIGHT      7
#define MAX_DIRECTION   8

const char DIR_NAME[][20] = {"UP LEFT", "UP", "UP RIGHT", "LEFT", "RIGHT", "DOWN_LEFT", "DOWN", "DOWN RIGHT"};
      
const int DIRECTION_SHIFT[] = { -11, -10, -9,
                                -1,        1,
                                 9,   10,  11 };
                                                            
const U64 RIGHT_BARRIER = 0b10000000001000000000100000000010000000001000000000;
const U64 LEFT_BARRIER  = 0b00000000010000000001000000000100000000010000000001;  





//Data types
//------------------------------------------------------------------------------

/* bit representation of the game board
   where each integer's lower 50 bits represent
   the presence of pieces for half the board
   e.g.                    
    there is a piece on (8, 3)
    - set ((8-5)*10 + 3)th bit in bottom to 1
    there is a piece on (3, 4)
    - set (3*10 + 4)th bit in top to 1                                      */
struct BitBoard
{
    U64 top;
    U64 bottom;
};

/* the game state, presented by two bitboards */
struct gameState
{
    BitBoard black;
    BitBoard white;
};





//Function declarations
//------------------------------------------------------------------------------


/* parse the board into a gameState structure
   #in#  b          ... the game board
   #out# blocked    ... output whether each square contains a block
   return           ... the parsed gameState structure                      */
gameState parseBoard(Board & b, BitBoard & blocked);


/* print the bitboard
   #in#  gs         ... the bit board to print
   #in#  indent     ... number of spaces at beginning of each line          */
void printBitBoard(BitBoard bb, int indent);


/* get the set of of available moves for the current player
   #in#  gs         ... the current gameState
   #in#  blocked    ... bit board for the blocked squares
   #in#  player     ... the current player
   #out# moves      ... the list of valid moves
   #out# effect     ... the bits the move will flip
   return           ... number of available moves                           */
int getMoves(gameState gs, BitBoard blocked, BoardState player, int moves[], BitBoard effect[]);


/* return the index of the asked move */ 
int getMoveIndex(int m, int moves[], int mCount);


/* print the current board, use x for black, o for white, * for blocks
   #in#  gs         ... the current gameState
   #in#  blocked    ... true indicates each square is blocked
   #in#  indent     ... number of spaces at beginning of each line          */
void printGameState(gameState gs, BitBoard blocked, int indent);


/* apply the player's move, flip bits and return new game state
   #in#  gs         ... the current gameState
   #in#  player     ... the moving player
   #in#  index      ... the move index
   #in#  flipbits   ... the calculated effect of the move                       
   return           ... the new gameState                                     */
gameState applyMove(const gameState & gs, BoardState player,
                    int index,    const BitBoard & flipbits);                     


/* parse the array notation of the board into bit board representation
   #in#  arr        ... array representation, each element is
                        {EMPTY, WHITE, BLACK, BLOCKED}
   #in#  color      ... the color to parse
   return           ... the bit board of the chosen color                     */
BitBoard ArrayToBitBoard(BoardState arr[][10], BoardState color);

/* bit shift operator for BitBoard */
BitBoard operator>>(const BitBoard & bb, int i);
BitBoard operator<<(const BitBoard & bb, int i); 





//Inline functions
//------------------------------------------------------------------------------

static inline void SET_BIT(BitBoard & bb, int index)
{   
    if (index < 50)
    {
        bb.top |= (1LL << index);
    }
    else
    {
        bb.bottom |= (1LL << (index - 50));
    }
}


static inline bool GET_BIT(const BitBoard & bb, int index)
{
    if (index < 50)
    {
        return bb.top & (1LL << index);
    }
    
    return bb.bottom & (1LL << (index - 50));
}

static inline BitBoard operator|(const BitBoard & b1, const BitBoard & b2)
{
    BitBoard result;
    
    result.top = b1.top | b2.top;
    result.bottom = b1.bottom | b2.bottom;

    return result;
}

static inline BitBoard operator&(const BitBoard & b1, const BitBoard & b2)
{
    BitBoard result;
    
    result.top = b1.top & b2.top;
    result.bottom = b1.bottom & b2.bottom;

    return result;
}

static inline BitBoard operator~(const BitBoard & b1)
{
    BitBoard result;
    result.top = ~b1.top;
    result.bottom = ~b1.bottom;
  
    return result;
}

static inline bool operator==(const BitBoard & bb, U64 i)
{
    return (bb.top == i) && (bb.bottom == 0);
}

static inline bool operator!=(const BitBoard & bb, U64 i)
{
    return (bb.top != i) || (bb.bottom != 0);
}

static inline BitBoard operator^(const BitBoard & b1, const BitBoard & b2)
{
    BitBoard result;
    
    result.top = b1.top ^ b2.top;
    result.bottom = b1.bottom ^ b2.bottom;
    
    return result;
}

static inline int BitCount(U64 n)
{
    int count = 0;
    while (n != 0)
    {
        n &= n - 1;
        count ++;
    }
    return count;
}

static inline int BitCount(BitBoard bb)
{
    return BitCount(bb.top) + BitCount(bb.bottom);
}

static inline bool operator==(const gameState & gs1, const gameState & gs2)
{
    return  gs1.black.top == gs2.black.top          &&
            gs1.black.bottom == gs2.black.bottom    &&
            gs1.white.top == gs2.white.top          &&
            gs1.white.bottom == gs2.white.bottom;
}

static inline bool operator==(const BitBoard & b1, const BitBoard & b2)
{
    return b1.top == b2.top && b1.bottom == b2.bottom;
}

static inline void operator|=(BitBoard & b1, const BitBoard & b2)
{
    b1.top |= b2.top;
    b1.bottom |= b2.bottom;
}

static inline void operator&=(BitBoard & b1, const BitBoard & b2)
{
    b1.top &= b2.top;
    b1.bottom &= b2.bottom;
}
